import CustomEvent = WechatMiniprogram.CustomEvent;
import {BasePage, BasePageData} from "../common/core/BasePage";
import {DataLoader, field} from "../../modules/coreModule/data/DataLoader";
import {Trade} from "../../modules/payModule/data/Trade";
import {Contract} from "../../modules/payModule/data/Contract";
import {BaseData, PK, pkType} from "../../modules/coreModule/data/BaseData";
import {page, pageFunc} from "../common/PageBuilder";
import {ItemDetailPage} from "../common/pages/ItemDetailPage";
import {AuthPage} from "../common/partPages/UserPage";
import {UploaderPage} from "../common/partPages/UploaderPage";
import {waitForLogin} from "../../modules/userModule/managers/UserManager";
import {PayInterface} from "../../modules/payModule/PayCommon";
import {Bill, BillStatus, PayType} from "../../modules/payModule/data/Bill";
import {handleError} from "../../modules/coreModule/managers/AlertManager";
import {pageMgr} from "../../modules/coreModule/managers/PageManager";
import {PayResultPage} from "../payResult/PayResultPage";
import {TemplateIds} from "../../modules/messageModule/MessageCommon";

class Data extends BasePageData {

	@field(Trade)
	trade: Trade;
	@field(Contract)
	contract: Contract;

	// 展示页（false => Trade, true => Contract）
	@field(Boolean)
	contractPage: boolean = false

	@field(pkType())
	billId: PK;
	@field(Object)
	paymentData: object;

	@field
	emailSend: boolean = false;
}

class Param extends BaseData {
	tradeId?: PK
	contractId?: PK
	onSuccess?: () => any
}

@page("payment", "支付")
export class PaymentPage extends BasePage<Data, Param> {

	public data = new Data();
	// public params: {
	// 	id: PK, //, depositId?: string
	// 	nextPage: string,
	// 	nextParams: any
	// }

	/**
	 * 部分页
	 */
	public userPage: AuthPage = new AuthPage();
	public uploaderPage: UploaderPage = new UploaderPage("contract", "contract");

	/**
	 * 测试函数
	 */
	async test() {
		// 创建订单测试
		// const res = await PayInterface.CreateTrade({
		// 	price: 1, description: "测试交易"
		// });
		// const trade = DataLoader.load(Trade, res.trade);
		// await this.setItem(trade);

		// 检查订单测试
		// const res = await PayInterface.UpdateBill({
		// 	billId: this.item.lastBill._id
		// });
		// console.log("UpdateBill", res);
		// await this.loadData();
	}

	/**
	 * OnLoad
	 */
	async onLoad(e) {
		super.onLoad(e);
		await this.loadData();
		await this.test();
	}

	/**
	 * 加载数据
	 */
	@waitForLogin
	private async loadData() {
		const tRes = await PayInterface.GetTrade({tradeId: this.params.tradeId});
		const cRes = await PayInterface.GetContract({contractId: this.params.contractId});

		let trade, contract;
		if (tRes.trade)
			trade = DataLoader.load(Trade, tRes.trade);
		if (cRes.contract)
			contract = DataLoader.load(Contract, cRes.contract);

		const contractPage = !trade;

		await this.setData({ trade, contract, contractPage })

		// let deposit = null; // 查询押金（是否支付押金）
		// if (trade.depositId) {
		// 	res = await PayInterface.GetDeposits({
		// 		filter: {_id: trade.depositId}
		// 	});
		// 	deposit = DataLoader.load(Deposit, res.deposits[0]);
		// 	// 这里只是保证前端type为Deposit，数据库不一定
		// 	trade.type = TradeType.Deposit;
		// }
		//
		// return this.setData({
		// 	item: trade, deposit,
		// 	depositType: DepositType.Direct
		// });
	}

	// region 事件

	// region 输入

	@pageFunc
	public onEmailChange(e: CustomEvent<any>) {
		this.setData({["contract.email"]: e.detail.value}).then();
	}

	@pageFunc
	public onNameChange(e: CustomEvent<any>) {
		this.setData({["contract.name"]: e.detail.value}).then();
	}

	@pageFunc
	public onCardNoChange(e: CustomEvent<any>) {
		this.setData({["contract.cardNo"]: e.detail.value}).then();
	}

	// endregion

	// region 数据

	/**
	 * 快捷获取
	 */
	public get trade() { return this.data.trade; }
	public get contract() { return this.data.contract; }

	/**
	 * 只有一种方式
	 */
	public get isSingle() {
		return (!this.trade && this.contract) ||
			(this.trade && !this.contract);
	}

	// endregion

	/**
	 * 切换交易模式
	 */
	@pageFunc
	public switchPayment() {
		this.setData({
			contractPage: !this.data.contractPage
		})
	}

	/**
	 * TODO:获取合同
	 */
	@pageFunc
	@handleError
	public async createContract() {
		this.checkContractInfo();
		await PayInterface.RequestContract({
			contractId: this.data.contract._id,
			email: this.data.contract.email,
			name: this.data.contract.name,
			card: this.data.contract.cardNo
		})
		await this.setData({
			emailSend: true
		});
	}

	private checkContractInfo() {
		if (!this.data.contract.email) throw "请输入邮箱";
		if (!this.data.contract.name) throw "请输入主体名称";
		if (!this.data.contract.cardNo) throw "请输入证件号码";
		// TODO: 检查Email格式
	}

	/**
	 * TODO: 提交
	 */
	@pageFunc
	public submit() {
		wx.requestSubscribeMessage({
			tmplIds: [TemplateIds["审核通知"]],
			success: this.doSubmit.bind(this)
		})
	}
	@handleError
	private async doSubmit() {
		if (this.data.contract && this.data.contractPage)
			await this.submitContract();
		if (this.data.trade && !this.data.contractPage)
			await this.submitTrade();
	}

	// endregion

	/**
	 * 提交押金
	 */
	// private async submitDeposit() {
	// 	let res = await PayInterface.PayDeposit({
	// 		taskId: this.data.deposit._id,
	// 		payType: PayType.WeChat,
	// 	});
	// 	await this.requestPayment(res.bill._id, res.data);
	// 	this.toResult();
	// }

	/**
	 * 提交合同
	 */
	@handleError
	private async submitContract() {
		this.checkSubmit();
		await this.uploadPicture();
		await PayInterface.SubmitContract({
			contractId: this.data.contract._id,
			name: this.data.contract.name,
			cardNo: this.data.contract.cardNo,
			picture: this.data.contract.picture
		})
		delete this.params.tradeId;
		await this.toResult();
	}

	/**
	 * 校验格式
	 */
	private checkSubmit() {
		if (this.uploaderPage.data.images.length <= 0)
			throw "未上传图片！";
		// 检查身份证格式
		// if (!PaymentPage.checkCardNo(this.data.contract.cardNo))
		// 	throw "请输入有效身份证号码";
	}

	/**
	 * 上传图片
	 */
	private async uploadPicture() {
		const fileIds = await this.uploaderPage.uploadPictures();
		await this.setData({["contract.picture"]: fileIds[0]});
	}

	/**
	 * 提交交易
	 */
	private async submitTrade() {
		let billId = this.data.billId, data = this.data.paymentData;
		if (!data) { // 如果未生成订单
			let res = await PayInterface.CreateBill({
				tradeId: this.data.trade._id,
				payType: PayType.WeChat,
				description: this.data.trade.description
			});
			billId = res.bill._id; data = res.data;
		}
		if (await this.requestPayment(billId, data)) {
			delete this.params.contractId;
			await this.toResult();
		} else
			await this.setData({
				billId, paymentData: data
			})
	}

	/**
	 * 发起微信支付
	 */
	private async requestPayment(billId, data) {
		try {
			let res: any = await wx.requestPayment(data);
			console.log("Pay: ", res);
			res = await PayInterface.UpdateBill({billId});
			return res.bill.status == BillStatus.Success;
		} catch (e) {
			console.log("Pay Error:", e);
			return false;
		}
	}

	/**
	 * 跳转到结果页
	 */
	private async toResult() {
		await pageMgr().change(PayResultPage, this.params);
	}

	// region 身份证号码验证

	/**
	 * 18位二代身份证号码的正则表达式
	 */
	private static REGEX_ID_NO_18: RegExp = /^\d{6}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/

	/**
	 * 15位一代身份证号码的正则表达式
	 */
	private static REGEX_ID_NO_15: RegExp = /^\d{6}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$/

	/**
	 * 校验身份证号码
	 * @param cardNo 身份证号码
	 * @return true - 校验通过<br>
	 *         false - 校验不通过
	 */
	private static checkCardNo(cardNo: string): boolean {
		return (cardNo.length == 18 //检测二代身份证份证是否有18位
				&& PaymentPage.REGEX_ID_NO_18.test(cardNo)  // 匹配身份证号码的正则表达式
				&& this.validateCheckNumber(cardNo)) // 校验身份证号码的验证码
			|| (cardNo.length == 15 //检测一代身份证是否有15位
				&& PaymentPage.REGEX_ID_NO_15.test(cardNo)); // 检测一代身份证是否匹配正则表达式
	}

	/**
	 * 校验18位身份证的校验码
	 */
	private static validateCheckNumber(cardNo18: string): boolean {
		let W: number[] = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
		let cardNo18Array: string[] = cardNo18.split("");
		let sum: number = 0;
		for (let i = 0; i < W.length; i++) {
			sum += Number(cardNo18Array[i]) * W[i];
		}

		// 校验位是X则表示10 该位的加权因子为1
		if (cardNo18Array[17] == 'X' || cardNo18Array[17] == 'x')
			sum += 10;
		else
			sum += Number(cardNo18Array[17]);

		//如果除11模1，则校验通过
		return sum % 11 == 1;
	}

	// endregion
}
